use common_base::prelude::*;
use tonic::transport::Endpoint;

use crate::{
    data::{DataObject, ParamObject, ResponseObject},
    invoker::remote::remote_invoke::{remote_invoker_client::RemoteInvokerClient, InvokeRequest},
};

pub struct InvokerClient;

impl InvokerClient {
    pub fn initialize() {
        // TODO: share client connection pool
    }

    pub async fn remote_call(
        uri: String,
        active_invoker_name: String,
        param_object: ParamObject,
        data_object: Option<DataObject>,
    ) -> Result<ResponseObject> {
        let mut client = RemoteInvokerClient::connect(Endpoint::from_shared(uri)?).await?;

        let param_obj_bytes = param_object.serializes_into_bincode()?;
        let data_obj_bytes = if let Some(data_obj) = data_object {
            Some(data_obj.serializes_into_bincode()?)
        } else {
            None
        };
        let request = tonic::Request::new(InvokeRequest {
            active_invoker_name: active_invoker_name,
            param_object: param_obj_bytes,
            data_object: data_obj_bytes,
            custom_object: None,
            addon_object: None,
        });

        let response = client.invoke(request).await?;

        let invoke_response = response.into_inner();
        if invoke_response.code == 1 {
            ResponseObject::deserizlize_from_bincode(invoke_response.response_object_bytes)
        } else {
            Err(ErrorCode::RemoteInvokerError(format!(
                "Remote invoke failed, get code: {} and message: {}",
                invoke_response.code, invoke_response.message
            )))
        }
    }
}
